﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Security;
using System.Security.AccessControl;
using System.Security.Principal;

namespace MsExchangeBcsConnector.ExchangeBdcModel
{
    internal static class MailBoxSecurityCache
    {       
        
        private static Dictionary<string, SecurityDescriptionCacheEntry> mailboxSecurities = new Dictionary<string, SecurityDescriptionCacheEntry>();
        private static Timer globalTimer = new Timer(new TimerCallback(MailBoxSecurityCache.CleanupUnusedSDEntries), null, 0, 60 * 1000);
        private const int CacheExpirationTime= 10;// in sec
       
        private static void CleanupUnusedSDEntries(object state)
        {
            DateTime time = DateTime.Now.Subtract(new TimeSpan(0,CacheExpirationTime,0));
            lock (mailboxSecurities)
            {
                 var expiredEntries = from entry in mailboxSecurities where entry.Value.LastAccessedTime < time
                                      select entry.Key;

                 string[] expiredKeys = expiredEntries.ToArray<string>();
                 if (expiredKeys != null)
                 foreach (string key in expiredKeys)
                             mailboxSecurities.Remove(key);         

                
            }
        }

        internal static void CacheMailboxSecurity(string EMail, byte[] sd)
        {
            if (string.IsNullOrEmpty(EMail)){
                return;
            }
            lock(mailboxSecurities){

                if (!mailboxSecurities.ContainsKey(EMail))
                {
                    mailboxSecurities[EMail] = new SecurityDescriptionCacheEntry();
                }

                mailboxSecurities[EMail].SecurityDescriptor = sd;
            }
        }
        
        internal static byte[] GetMailboxSecurity(string EMail)
        {
            if (mailboxSecurities.ContainsKey(EMail))
            {
                lock (mailboxSecurities)
                {
                    mailboxSecurities[EMail].LastAccessedTime = DateTime.Now;
                }
                return mailboxSecurities[EMail].SecurityDescriptor;
            }
            else
                return null;
        }

        internal static byte[] CacheMailboxSecurity(MailBox mailBox)
        {
 
            SecurityIdentifier sid = null;
            CommonSecurityDescriptor sd = null;

            string[] Logins = mailBox.Login.Split(MailBox.LoginSeparator);
            foreach (string sL in Logins)
            {   //Grant Access for User Login account                
                 sid = SIDfromLogin(sL);
                if (sd == null)
                {
                    sd = new CommonSecurityDescriptor(false, false, ControlFlags.None, sid, null, null, null);
                    sd.SetDiscretionaryAclProtection(true, false);
                    //Deny access to all users.
                    SecurityIdentifier everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
                    sd.DiscretionaryAcl.RemoveAccess(AccessControlType.Allow, everyone,
                 unchecked((int)0xffffffffL), InheritanceFlags.None, PropagationFlags.None);
                }
                sd.DiscretionaryAcl.AddAccess(AccessControlType.Allow, sid,
                                 unchecked((int)0xffffffffL), InheritanceFlags.None, PropagationFlags.None);
            }


            byte[] secDes = new Byte[sd.BinaryLength];
            sd.GetBinaryForm(secDes, 0);

            CacheMailboxSecurity(mailBox.EMail, secDes);
            return secDes;
           // mailBox.SecurityDesciptor = secDes;
        }

        internal static SecurityIdentifier SIDfromLogin(string Login)
        {
            // replace wrong slash
            Login = Login.Replace('/', '\\');
            string[] Dl = Login.Split('\\');
            NTAccount workerAcc = new NTAccount(Dl[0], Dl[1]);

            return (SecurityIdentifier)workerAcc.Translate(typeof(SecurityIdentifier));
        }

        /*  internal void SecureByTemplate(SecureEntity SecurityTemplate)
          {
              if (!string.IsNullOrEmpty(SecurityTemplate.OwnerAccount))
                  this.OwnerAccount = SecurityTemplate.OwnerAccount;

              if (!string.IsNullOrEmpty(SecurityTemplate.Login))
              {
                  this.Login = SecurityTemplate.Login;
              }
          }*/

    }
}
